import pandas as pd
import re
from typing import List, Dict, Any, Optional, Union
from datetime import datetime
from .logger import get_logger

logger = get_logger("Validators")


class ValidationError(Exception):
    """数据验证异常"""
    pass


class DataValidator:
    """数据验证器"""

    @staticmethod
    def validate_symbol(symbol: str) -> bool:
        """验证股票代码格式（仅限A股）"""
        if not symbol or not isinstance(symbol, str):
            return False

        # 清理股票代码
        symbol = symbol.strip().upper()

        # 只允许A股代码（6位数字）
        if not re.match(r'^\d{6}$', symbol):
            return False

        # 检查A股代码范围
        valid_prefixes = [
            '60',  # 上海主板
            '68',  # 科创板
            '00',  # 深圳主板
            '30',  # 创业板
            '8',   # 北交所（8开头）
            '4',   # 北交所（4开头）
        ]

        # 检查是否以有效前缀开头
        for prefix in valid_prefixes:
            if symbol.startswith(prefix):
                return True

        return False

    @staticmethod
    def validate_interval(interval: str) -> bool:
        """验证数据间隔格式"""
        valid_intervals = ['1d', '1h', '30m', '15m', '5m']
        return interval in valid_intervals

    @staticmethod
    def validate_date_format(date_str: str) -> bool:
        """验证日期格式"""
        try:
            datetime.strptime(date_str, '%Y-%m-%d')
            return True
        except ValueError:
            return False

    @staticmethod
    def validate_dataframe(data: pd.DataFrame, symbol: str, interval: str) -> List[str]:
        """验证DataFrame数据格式"""
        errors = []

        if data.empty:
            errors.append(f"Data is empty for {symbol}")
            return errors

        # 检查必需的列
        required_columns = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']
        missing_columns = [col for col in required_columns if col not in data.columns]

        if missing_columns:
            errors.append(f"Missing required columns: {missing_columns}")

        # 检查数据类型
        numeric_columns = ['Open', 'High', 'Low', 'Close', 'Volume']
        for col in numeric_columns:
            if col in data.columns:
                if not pd.api.types.is_numeric_dtype(data[col]):
                    errors.append(f"Column {col} should be numeric")

        # 检查价格数据的合理性
        price_columns = ['Open', 'High', 'Low', 'Close']
        for col in price_columns:
            if col in data.columns:
                if data[col].min() <= 0:
                    errors.append(f"Column {col} contains invalid values (<= 0)")

        # 检查成交量数据
        if 'Volume' in data.columns:
            if data['Volume'].min() < 0:
                errors.append("Volume contains negative values")

        # 检查价格逻辑关系
        if all(col in data.columns for col in ['High', 'Low']):
            invalid_rows = data[data['High'] < data['Low']]
            if not invalid_rows.empty:
                errors.append(f"High < Low in {len(invalid_rows)} rows")

        if all(col in data.columns for col in ['High', 'Low', 'Open', 'Close']):
            invalid_rows = data[
                (data['High'] < data['Open']) |
                (data['High'] < data['Close']) |
                (data['Low'] > data['Open']) |
                (data['Low'] > data['Close'])
            ]
            if not invalid_rows.empty:
                errors.append(f"Price relationships invalid in {len(invalid_rows)} rows")

        return errors

    @staticmethod
    def clean_dataframe(data: pd.DataFrame) -> pd.DataFrame:
        """清理DataFrame数据"""
        if data.empty:
            return data

        # 删除重复行
        data = data.drop_duplicates()

        # 处理缺失值
        numeric_columns = ['Open', 'High', 'Low', 'Close', 'Volume']
        for col in numeric_columns:
            if col in data.columns:
                # 用前一个有效值填充
                data[col] = data[col].fillna(method='ffill')
                # 如果仍然有缺失值，用0填充
                data[col] = data[col].fillna(0)

        # 确保数值列的数据类型
        for col in numeric_columns:
            if col in data.columns:
                data[col] = pd.to_numeric(data[col], errors='coerce')

        # 删除所有列都为空的行
        data = data.dropna(how='all')

        return data

    @staticmethod
    def validate_config(config: Dict[str, Any]) -> List[str]:
        """验证配置文件"""
        errors = []

        # 检查必需的顶级键
        required_keys = ['system', 'data_fetch', 'storage']
        for key in required_keys:
            if key not in config:
                errors.append(f"Missing required config section: {key}")

        # 验证系统配置
        if 'system' in config:
            system_config = config['system']
            if 'version' not in system_config:
                errors.append("Missing system.version")
            if 'data_directory' not in system_config:
                errors.append("Missing system.data_directory")
            if 'log_level' not in system_config:
                errors.append("Missing system.log_level")
            else:
                valid_levels = ['DEBUG', 'INFO', 'WARNING', 'ERROR']
                if system_config['log_level'] not in valid_levels:
                    errors.append(f"Invalid log_level: {system_config['log_level']}")

        # 验证数据获取配置
        if 'data_fetch' in config:
            fetch_config = config['data_fetch']
            if 'default_interval' not in fetch_config:
                errors.append("Missing data_fetch.default_interval")
            elif not DataValidator.validate_interval(fetch_config['default_interval']):
                errors.append(f"Invalid default_interval: {fetch_config['default_interval']}")

            if 'max_retries' not in fetch_config:
                errors.append("Missing data_fetch.max_retries")
            elif not isinstance(fetch_config['max_retries'], int) or fetch_config['max_retries'] < 0:
                errors.append("Invalid max_retries value")

            if 'request_delay' not in fetch_config:
                errors.append("Missing data_fetch.request_delay")
            elif not isinstance(fetch_config['request_delay'], (int, float)) or fetch_config['request_delay'] < 0:
                errors.append("Invalid request_delay value")

        # 验证存储配置
        if 'storage' in config:
            storage_config = config['storage']
            if 'file_format' not in storage_config:
                errors.append("Missing storage.file_format")
            elif storage_config['file_format'] != 'csv':
                errors.append(f"Unsupported file_format: {storage_config['file_format']}")

        return errors

    @staticmethod
    def validate_time_format(time_str: str) -> bool:
        """验证时间格式 (HH:MM)"""
        try:
            datetime.strptime(time_str, '%H:%M')
            return True
        except ValueError:
            return False

    @staticmethod
    def validate_timezone(timezone: str) -> bool:
        """验证时区格式"""
        try:
            import pytz
            return timezone in pytz.all_timezones
        except ImportError:
            # 如果没有pytz，只做基本格式验证
            pattern = r'^[A-Za-z]+/[A-Za-z_]+$'
            return bool(re.match(pattern, timezone))